// 1827. 【软件认证】重组乱序的数据分段
// https://oj.rnd.huawei.com/problems/1827/details
// 未通过,需要去重
//某协议的数据包在发送时会被分为若干数据分段，接收到的数据分段可能出现重复、丢失、乱序。
//每个数据分段的组成格式为：数据类型 分段序号 结束标志 内容长度 净荷内容
// 0 数据类型：数据分段的数据类型，取值范围[0,9]
// 1 分段序号：数据分段在数据包中的序号，取值范围[0,100000]
// 1 结束标志：表示此数据分段是否为数据包的最后一个分段，是为1，否为0
// 5 净荷长度：净荷内容的长度，取值范围[2,5]
//净荷内容：此数据分段的实际内容
//现给定一批接收到的数据分段和某一数据类型，请重组该数据类型的数据包，重组及输出的规则如下：
//成功重组必须是完整的数据：“分段序号”从0开始且是连续的，并且仅序号最大的数据分段的“结束标志”为1。
//如果能成功重组，按“分段序号”升序，将该数据类型的“净荷内容”，拼接为一个字符串并输出。如果分段序号有重复则去重。
//如果不能成功重组，则输出字符串NA。
//输入 首行输入一个整数 N，表示数据分段的个数，值范围[1,1000000]
//接下来 N 行，每行输入一个数据分段
//最后一行是一个整数，代表需要重组的数据类型。
//注意：用例保证每个数据分段的内容都符合格式和范围要求，重复的数据分段保证整体是完全一样的，并且某数据类型的“结束标志”
// 1 仅可能出现在序号最大的数据分段。 输出 重组后的数据包内容，或字符串NA。
//<类序结长>
//输入 7
// 0 1 0 5 hello
// 1 2 1 2 CC
// 0 3 1 5 world
// 0 0 0 3 Say
// 1 0 0 2 AA
// 0 2 0 2 to
// 1 1 0 2 BB
// 0
//输出 Sayhellotoworld
//数据分段0 1 0 5
// hello，表示属于0号数据类型，分段序号为1，结束标志位0，净荷长度为5，净荷内容为
// hello。 最后一行输入0，表示需要重组0号数据类型的数据包。
// 0号数据类型的数据分段有0 1 0 5 hello, 0 3 1 5 world, 0 0 0 3 Say, 0 2 0 2
// to，按分段序号顺序重组，遇到结束标志1停止，则重组后的输出为Sayhellotoworld。
//输入 2
// 9 2 1 5 world
// 9 1 0 5 hello
// 9
//输出 NA
//分段序号缺少0，所以输出NA

// 9
// 0 1 0 5 hello
// 1 2 1 2 CC
// 0 3 1 5 world
// 0 2 0 2 to
// 0 0 0 3 Say
// 1 0 0 2 AA
// 0 2 0 2 to
// 0 3 1 5 world
// 1 1 0 2 BB
// 0

#include <bits/stdc++.h>
using namespace std;

#define DEBUG
#ifdef DEBUG
#define PF(...) printf(__VA_ARGS__)
#else
#define PF(...)
#endif

struct Package {
  int fragType;    // 数据类型
  int fragOffset;  // 分段序号 0 - max ,
  int finish;      //  结束标志 end=1
  int length;      // 长度
  string data;     // 数据
};

class Solution {
 public:
  // 待实现函数,在此函数中填入答题代码;
  string ReassembleData(const vector<Package> &records, int packageType) {
    for (auto [typ, offset, fini, len, data] : records)
      PF("%d,%d,%d,%s\n", typ, offset, fini, data.c_str());
    PF("\n");
    PF("Type=%d\n", packageType);
    const static string ERROR = "NA";
    vector<Package> vtRecord;
    for (auto pkg : records) {
      if (packageType == pkg.fragType) {
        vtRecord.push_back(pkg);
      }
    }
    std::sort(vtRecord.begin(), vtRecord.end(), [](Package p1, Package p2) {
      return p1.fragOffset < p2.fragOffset;
    });
    for (auto [typ, offset, fini, len, data] : vtRecord)
      PF("%d,%d,%d,%s\n", typ, offset, fini, data.c_str());
    PF("\n");
    if (vtRecord.size() == 0) {
      PF("size=%d\n", vtRecord.size());
      return ERROR;
    }
    string result;
    for (size_t i = 0; i < vtRecord.size(); i++) {
      auto [type, offset, fini, len, data] = vtRecord[i];
      if (i == vtRecord.size() - 1) {
        if (fini != 1) {
          PF("fini== =%d\n", fini);
          return ERROR;
        }
      } else {
        if (fini != 0) {
          PF("fini=%d\n", fini);
          return ERROR;
        }
      }
      if (offset != i) {
        PF("i= %d, offset=%d,%s\n", i, offset, data.c_str());
        return ERROR;
      }
      result += data.substr(0, len);
    }
    return result;
  }
};

// 以下为考题输入输出框架，此部分代码不建议改动
int main() {
  int num;
  cin >> num;

  vector<Package> records;
  for (int loop = 0; loop < num; loop++) {
    Package rec;
    cin >> rec.fragType >> rec.fragOffset >> rec.finish >> rec.length >>
        rec.data;
    records.push_back(rec);
  }
  int packageType;  // 指定数据类型
  cin >> packageType;

  Solution solu;
  string result = solu.ReassembleData(records, packageType);
  cout << result << endl;
  return 0;
}
